﻿namespace LinkedInWinRT.Helpers
{
    using System;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;
    using System.Net.Http.Headers;
    using System.Collections.Generic;

    using Windows.Foundation;
    using Windows.Security.Authentication.Web;

    using Client;
    using Models;
    public abstract class LinkedInLoginHelper
    {
        protected Dictionary<Permission, string> PermissionsDictionary;
        public List<Permission> Permissions
        {
            get
            {
                return new List<Permission>(_permissions);
            }
            protected set { _permissions = value; }
        }

        #region Requests URIs

        protected const string RequestTokenUrl = "https://api.linkedin.com/uas/oauth/requestToken";

        protected const string AccessTokenUrl = "https://api.linkedin.com/uas/oauth/accessToken";

        protected const string RequestConnections = "http://api.linkedin.com/v1/people/~/connections";

        protected const string NetworkUpdates = "http://api.linkedin.com/v1/people/~/network/updates?count=100";

        #endregion


        #region Credentials

        public Credentials Credentials { get; set; }

        #endregion

        private readonly OAuthUtil _oAuthUtil = new OAuthUtil();
        private string _link;
        private List<Permission> _permissions;

        #region Login Steps

        protected async Task GetRequestToken()
        {
            string nonce = _oAuthUtil.GetNonce();
            string timeStamp = _oAuthUtil.GetTimeStamp();

            string sigBaseStringParams = "oauth_callback=" + Uri.EscapeDataString(Credentials.CallbackUrl);
            sigBaseStringParams += "&" + "oauth_consumer_key=" + Credentials.ConsumerKey;
            sigBaseStringParams += "&" + "oauth_nonce=" + nonce;
            sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
            sigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
            sigBaseStringParams += "&" + "oauth_version=1.0";
            sigBaseStringParams += "&" + "scope=";
            if (Permissions == null || Permissions.Count == 0)
            {
                sigBaseStringParams += "r_basicprofile";
            }
            else
            {
                foreach (var permission in Permissions.Distinct())
                {
                    sigBaseStringParams += PermissionsDictionary[permission];
                    sigBaseStringParams += "%20";
                }
                int length = sigBaseStringParams.Length;
                sigBaseStringParams = sigBaseStringParams.Remove(length - 3, 3);
            }

            string sigBaseString = "POST&";
            sigBaseString += Uri.EscapeDataString(RequestTokenUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
            string signature = _oAuthUtil.GetSignature(sigBaseString, Credentials.ConsumerKeySecret);

            string responseText =
                await
                _oAuthUtil.PostData(RequestTokenUrl,
                                    sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));

            if (!string.IsNullOrEmpty(responseText))
            {
                GetOauthTokens(responseText);
                string result = await ShowLoginDialog();
                Credentials.Verifier = GetVerifier(result);
                await GetAccessToken();
            }
        }

        protected async Task<string> ShowLoginDialog()
        {
            if (_link != null)
            {
                WebAuthenticationResult result = await
                                                 WebAuthenticationBroker.AuthenticateAsync(
                                                     WebAuthenticationOptions.None, new Uri(_link),
                                                     new Uri(Credentials.CallbackUrl));
                return result.ResponseData;
            }
            return string.Empty;
        }

        protected string GetVerifier(string result)
        {
            if (result != null && !string.IsNullOrEmpty(result))
            {
                string[] keyValPairs = result.Split('?');
                string[] keys = keyValPairs[1].Split('&');
                return keys[1].Split('=')[1];
            }
            return string.Empty;
        }

        protected void GetOauthTokens(string responseText)
        {
            string oauthToken = null;
            string oauthTokenSecret = null;
            string oauthAuthorizeUrl = null;
            string[] keyValPairs = responseText.Split('&');

            for (int i = 0; i < keyValPairs.Length; i++)
            {
                String[] splits = keyValPairs[i].Split('=');
                switch (splits[0])
                {
                    case "oauth_token":
                        oauthToken = splits[1];
                        break;
                    case "oauth_token_secret":
                        oauthTokenSecret = splits[1];
                        break;
                    case "xoauth_request_auth_url":
                        oauthAuthorizeUrl = splits[1];
                        break;
                }
            }
            Credentials.RequestToken = oauthToken;
            Credentials.RequestTokenSecret = oauthTokenSecret;

            _link = Uri.UnescapeDataString(oauthAuthorizeUrl + "?oauth_token=" + oauthToken);
        }

        protected async Task GetAccessToken()
        {
            string nonce = _oAuthUtil.GetNonce();
            string timeStamp = _oAuthUtil.GetTimeStamp();

            string sigBaseStringParams = "oauth_consumer_key=" + Credentials.ConsumerKey;
            sigBaseStringParams += "&" + "oauth_nonce=" + nonce;
            sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
            sigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
            sigBaseStringParams += "&" + "oauth_token=" + Credentials.RequestToken;
            sigBaseStringParams += "&" + "oauth_verifier=" + Credentials.Verifier;
            sigBaseStringParams += "&" + "oauth_version=1.0";
            string sigBaseString = "POST&";
            sigBaseString += Uri.EscapeDataString(AccessTokenUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);

            // LinkedIn requires both consumer secret and request token secret
            string signature = _oAuthUtil.GetSignature(sigBaseString, Credentials.ConsumerKeySecret, Credentials.RequestTokenSecret);

            string responseText =
                await
                _oAuthUtil.PostData(AccessTokenUrl,
                                    sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));

            if (!string.IsNullOrEmpty(responseText))
            {
                string oauthToken = null;
                string oauthTokenSecret = null;
                string[] keyValPairs = responseText.Split('&');

                foreach (var keyValue in keyValPairs)
                {
                    String[] splits = keyValue.Split('=');
                    switch (splits[0])
                    {
                        case "oauth_token":
                            oauthToken = splits[1];
                            break;
                        case "oauth_token_secret":
                            oauthTokenSecret = splits[1];
                            break;
                    }
                }

                Credentials.AccessToken = oauthToken;
                Credentials.AccessTokenSecret = oauthTokenSecret;
            }
        }

        #endregion

        /// <summary>
        /// Makes a request to the specified url and returns
        /// a string formatted as an XML file
        /// </summary>
        /// <param name="url">The request url</param>
        /// <returns>XML with the requested data</returns>
        public async Task<string> ApiRequestAsync(string url)
        {
            string nonce = _oAuthUtil.GetNonce();
            string timeStamp = _oAuthUtil.GetTimeStamp();
            
            try
            {
                var httpClient = new HttpClient
                {
                    MaxResponseContentBufferSize = int.MaxValue
                };

                httpClient.DefaultRequestHeaders.ExpectContinue = false;

                var requestMsg = new HttpRequestMessage
                {
                    Method = new HttpMethod("GET"),
                    RequestUri = new Uri(url, UriKind.Absolute)
                };

                string sigBaseStringParams = "oauth_consumer_key=" + Credentials.ConsumerKey;
                sigBaseStringParams += "&" + "oauth_nonce=" + nonce;
                sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
                sigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
                sigBaseStringParams += "&" + "oauth_token=" + Credentials.AccessToken;
                sigBaseStringParams += "&" + "oauth_verifier=" + Credentials.Verifier;
                sigBaseStringParams += "&" + "oauth_version=1.0";
                string sigBaseString = "GET&";

                string uri = null;
                string queryString = string.Empty;

                try
                {
                    var decoder = new WwwFormUrlDecoder(new Uri(url).Query);
                    var uriBuilder = new UriBuilder(url);
                    uri = uriBuilder.Uri.ToString().Replace(uriBuilder.Query, "");
                    foreach (var parameter in decoder)
                    {
                        if (queryString == string.Empty)
                        {
                            queryString = "%26" + parameter.Name;
                            queryString += "%3D" + parameter.Value;
                            continue;
                        }
                        queryString += "%26" +parameter.Name + "%3D" + parameter.Value;
                    }
                }
                catch (Exception)
                {
                }
                if (uri == null)
                {
                    uri = url;
                }
                sigBaseString += Uri.EscapeDataString(uri) + "&" + Uri.EscapeDataString(sigBaseStringParams) + queryString;

                // LinkedIn requires both consumer secret and request token secret
                string signature = _oAuthUtil.GetSignature(sigBaseString, Credentials.ConsumerKeySecret, Credentials.AccessTokenSecret);

                string data = "realm=\"http://api.linkedin.com/\", oauth_consumer_key=\"" + Credentials.ConsumerKey
                              +
                              "\", oauth_token=\"" + Credentials.AccessToken +
                              "\", oauth_verifier=\"" + Credentials.Verifier +
                              "\", oauth_nonce=\"" + nonce +
                              "\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"" + timeStamp +
                              "\", oauth_version=\"1.0\", oauth_signature=\"" + Uri.EscapeDataString(signature) + "\"";
                requestMsg.Headers.Authorization = new AuthenticationHeaderValue("OAuth", data);
                HttpResponseMessage response = await httpClient.SendAsync(requestMsg);
                string text = await response.Content.ReadAsStringAsync();
                return text;
            }
            catch (Exception exception)
            {
                return exception.Message;
            }
        }
    }
}
